pub extern crate jnim;
pub extern crate xloop_types as types;

mod gen;
use app::AppRef;
pub use gen::*;

pub mod app;
pub mod error;
pub mod win;

pub mod prelude {
	pub use super::android;
	pub use super::init_jvm;
	pub use jnim;
}

#[derive(Debug)]
pub struct Application;
impl types::App for Application {
	type Error = error::Error;
	type Ref<'a> = app::AppRef;
	fn with(fun: impl FnOnce(Self::Ref<'_>)) {
		fun(AppRef);
	}
	fn run(rep: impl types::AppRep<Self>) -> Result<(), Self::Error> {
		app::app_init(rep);
		app::application_launched();
		Ok(())
	}
}
impl types::Log for Application {
	fn log(level: types::LevelFilter) {
		let level = match level {
			types::LevelFilter::Off => log::LevelFilter::Off,
			types::LevelFilter::Error => log::LevelFilter::Error,
			types::LevelFilter::Warn => log::LevelFilter::Warn,
			types::LevelFilter::Info => log::LevelFilter::Info,
			types::LevelFilter::Debug => log::LevelFilter::Debug,
			types::LevelFilter::Trace => log::LevelFilter::Trace,
		};
		android_logger::init_once(android_logger::Config::default().with_max_level(level));
	}
}
impl types::Fs for Application {
	fn read(path: impl AsRef<std::path::Path>) -> impl std::future::Future<Output = std::io::Result<Vec<u8>>> {
		use jnim::{JEnv, JObject};
		use std::io::{Error, ErrorKind};
		let Some(env) = JEnv::env(None) else {
			return ReadFuture::Err(Some(Error::new(ErrorKind::NotFound, "no env")));
		};
		let _frame: Option<jnim::JFrame<JObject>> = env.push_frame(32);
		let Some(assets) = app::AppHandle::assets() else {
			return ReadFuture::Err(Some(Error::new(ErrorKind::NotFound, "no assets")));
		};
		let Some(path) = path.as_ref().to_str() else {
			return ReadFuture::Err(Some(Error::new(ErrorKind::NotFound, "path to_str fail")));
		};
		let path_java = match std::ffi::CString::new(&*jnim::cesu8::to_java_cesu8(path)) {
			Ok(v) => v,
			Err(e) => {
				return ReadFuture::Err(Some(Error::new(ErrorKind::NotFound, e)));
			}
		};
		ReadFuture::Read(path_java, assets)
	}
}
pub enum ReadFuture {
	Read(std::ffi::CString, ndkm::Assets),
	Err(Option<std::io::Error>),
}
impl std::future::Future for ReadFuture {
	type Output = std::io::Result<Vec<u8>>;
	fn poll(mut self: core::pin::Pin<&mut Self>, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
		use std::io::{Error, ErrorKind};
		match &mut *self {
			ReadFuture::Read(path, assets) => {
				let mut bytes = vec![];
				match assets.read(path, &mut bytes) {
					Ok(_) => std::task::Poll::Ready(Ok(bytes)),
					Err(e) => std::task::Poll::Ready(Err(Error::new(ErrorKind::NotFound, e))),
				}
			}
			ReadFuture::Err(e) => {
				if let Some(e) = e.take() {
					std::task::Poll::Ready(Err(e))
				} else {
					std::task::Poll::Ready(Err(Error::new(ErrorKind::Other, "Pending error polled more than once")))
				}
			}
		}
	}
}

#[derive(Debug)]
pub struct Window;

impl types::Win for Window {
	type Error = error::Error;
	type WinRaw = win::WinRaw;
	type WinRef<'a> = win::WinRef<'a>;
	type WinHandle = win::WinHandle;
	type Mouse<'a> = win::WinEvent<'a>;
	type Wheel<'a> = win::WinEvent<'a>;
	type Keyboard<'a> = win::WinKeyboard<'a>;
	type Touch<'a> = win::WinTouch<'a>;
	type Ime<'a> = &'a types::Ime;
	type Attr = types::XwinAttr;
	fn win(key: impl AsRef<str>, _: Self::Attr, rep: impl types::WinRep<Self> + 'static) -> Result<Self::WinHandle, Self::Error> {
		win::reg_window(key.as_ref(), std::rc::Rc::new(rep));
		Ok(win::WinHandle(key.as_ref().to_string()))
	}
}

#[no_mangle]
#[allow(unused_unsafe)]
pub extern "C" fn init_jvm(jvm: *mut core::ffi::c_void) -> jnim::JInt {
	use jnim::*;
	JVm::singleton(JVm::from_void(jvm));
	JVm::DEFAULT_VERSION
}
